----------------------------------------------------------------------------------
-- CPEG 422 spartan-3e base
-- Jason Parrott
-- Rodney McGee
-- Ray Delvecchio
-- Juergen Ributzka
-- Jack Welsh
-- Ryan Hoover
-- Mike Stamat
-- Lee Reed
--
-- LCD Wrapper
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;


entity LCD_top is
  port ( clk            : in  std_logic;
         reset          : in  std_logic;
         keyboard_data  : in  std_logic_vector (7 downto 0);
         keyboard_ready : in  std_logic;
         LCD_D          : out std_logic_vector (11 downto 8);
         LCD_E          : out std_logic;
         LCD_RS         : out std_logic;
         LCD_RW         : out std_logic);
end LCD_top;

architecture Behavioral of LCD_top is

  component LCD
    port ( clk      : in  std_logic;
           reset    : in  std_logic;
           data_in  : in  std_logic_vector (7 downto 0);
           enable   : in  std_logic;
           LCD_D    : out std_logic_vector (11 downto 8);
           LCD_E    : out std_logic;
           finished : out std_logic);
  end component;

  signal data_in       : std_logic_vector (7 downto 0) := (others => '0');
  signal enable        : std_logic                     := '0';
  signal finished      : std_logic;
  signal finished_prev : std_logic;

  type statetype is (S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13);
  signal state, next_state : statetype := S0;

begin

  LCD_RW <= '0';
  state  <= next_state;

  UUT : LCD port map(clk, reset, data_in, enable, LCD_D, LCD_E, finished);

  process (clk, reset) is
                         variable addrCount  : std_logic_vector (3 downto 0) := "0000";
                       variable   secondLine : std_logic                     := '0';
  begin
    if (reset = '1') then
      enable        <= '0';
      addrCount                                                              := "0000";
      secondLine                                                             := '0';
      next_state    <= S0;
      finished_prev <= '0';
      LCD_RS        <= '0';
    elsif (rising_edge(clk)) then

      LCD_RS <= '0';

      case state is

                                        --States S0-S5: Display Configuration
                                        --S0: If rising edge of finished is detected, write data
                                        --0x28 = Function set - Spartan3E only supports this command
        when S0 =>
          if (finished = '1' and finished_prev = '0') then
            data_in    <= x"28";
            enable     <= '1';
            next_state <= S1;
          else
            next_state <= S0;
          end if;

                                        --S1: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing
        when S1 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S2;
          else
            next_state <= S1;
          end if;

                                        --S2: 0x01 = Clear Display
                                        --Clear display and return cursor to home position
        when S2 =>
          LCD_RS     <= '0';
          secondLine := '0';
          data_in    <= x"01";
          enable     <= '1';
          next_state <= S3;

                                        --S3: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing                                                          
        when S3 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S4;
          else
            next_state <= S3;
          end if;


                                        --S4: 0x06 = Entry Mode Set
                                        --              Set to auto-increment DDRAM address counter
                                        --              Disable shifting of characters
        when S4 =>
          data_in    <= x"06";
          enable     <= '1';
          next_state <= S5;

                                        --S5: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing  
        when S5 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S6;
          else
            next_state <= S5;
          end if;

                                        --S6: 0x0F = Display On/Off
                                        --              Display/Cursor/Blinking all on
        when S6 =>
          data_in    <= x"0F";
          enable     <= '1';
          next_state <= S7;

                                        --S7: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing  
        when S7 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S8;
          else
            next_state <= S7;
          end if;

                                        --S8: Set DDRAM address for 1st or 2nd line
        when S8 =>
          if (addrCount = "1111" and secondLine = '1') then
            data_in <= x"C0";
            addrCount := "0000";
          else
            data_in <= x"80";
            addrCount := "0000";
          end if;

          enable     <= '1';
          next_state <= S9;

                                        --S9: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing                                                          
        when S9 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S10;
          else
            next_state <= S9;
          end if;

                                                --S10: Process keyboard input
        when S10 =>
          if(keyboard_ready = '1') then
            if (keyboard_data = x"00") then     -- Unknown character
              next_state <= S10;
            elsif (keyboard_data = x"08") then  -- Backspace
              next_state <= S12;
            elsif (keyboard_data = x"0d") then  -- Return
              next_state <= S2;
            else
              LCD_RS     <= '1';
              data_in    <= keyboard_data;
              enable     <= '1';
              next_state <= S11;
            end if;
          else
            LCD_RS       <= '0';
            enable       <= '0';
            next_state   <= S10;
          end if;

        when S11 =>
          LCD_RS         <= '1';
          if (finished = '1' and finished_prev = '0') then
            enable       <= '0';
            if(addrCount = "1111") then
              secondLine := not secondLine;
              next_state <= S8;
            else
              addrCount  := addrCount + 1;
              next_state <= S10;
            end if;
          else
            next_state   <= S11;
          end if;

                                        --S12: Move cursor one left
        when S12 =>
          LCD_RS  <= '0';
          data_in <= x"10";
          addrCount := addrCount - 1;

          enable     <= '1';
          next_state <= S13;

                                        --S13: If rising edge of finish is detected, write is complete
                                        --              Set enable low for one cycle before continuing                                                          
        when S13 =>
          if (finished = '1' and finished_prev = '0') then
            enable     <= '0';
            next_state <= S10;
          else
            next_state <= S13;
          end if;
      end case;

      finished_prev <= finished;
    end if;
  end process;

end Behavioral;
